﻿using MudEngine.Runtime;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MudEngine.Collections
{
    /// <summary>
    /// List of WeakRefernces to objects
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class WeakReferenceList : IEnumerable<string>
    {
        private Dictionary<string, WeakReference> _internal;
        private Dictionary<long, object_t> _locked;
        private TimeSpan _cache;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="capacity"></param>
        public WeakReferenceList(int capacity, int cacheSeconds)
        {
            _internal = new Dictionary<string, WeakReference>(capacity);
            _locked = new Dictionary<long, object_t>(capacity);
            _cache = new TimeSpan(0, 0, cacheSeconds);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public object_t this[string name]
        {
            get
            {
                if (_internal.ContainsKey(name) && _internal[name].IsAlive)
                {
                    object_t obj = (object_t)_internal[name].Target;
                    this.KeepAlive(obj);
                    return obj;
                }
                return null;
            }
            set
            {
                if (_internal.ContainsKey(name))
                {
                    if (!_internal[name].IsAlive)
                        _internal[name] = new WeakReference(value);
                    else
                        throw new ArgumentException("Object already exists in list");
                }
                else
                    _internal.Add(name, new WeakReference(value));
                this.KeepAlive(value);
            }
        }

        private void KeepAlive(object_t obj)
        {
            if (!this._locked.ContainsKey(obj.Id))
                this._locked.Add(obj.Id, obj);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public bool ContainsKey(string name)
        {
            return _internal.ContainsKey(name) && _internal[name].IsAlive;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public IEnumerator<string> GetEnumerator()
        {
            return _internal.Keys.GetEnumerator();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return _internal.Keys.GetEnumerator();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        internal bool RemoveKey(string key)
        {
            return _internal.Remove(key);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="ticks"></param>
        /// <returns></returns>
        public bool Clean(long ticks)
        {
            long _s = DateTime.Now.Ticks;
            bool removeReferencesToBeDone = true;
            bool expireObjectsToBeDone = true;

            while (DateTime.Now.Ticks - _s < ticks && (removeReferencesToBeDone || expireObjectsToBeDone))
            {
                KeyValuePair<long, object_t> vpLocked = _locked.FirstOrDefault(lck => !lck.Value.PreventDestroy && (DateTime.Now - lck.Value.LastUsed) > _cache);
                if (vpLocked.Key > 0)
                {
                    _locked.Remove(vpLocked.Key);
                    Console.WriteLine("Object expired: " + vpLocked.Value.Name);
                    expireObjectsToBeDone = true;
                }
                else
                    expireObjectsToBeDone = false;

                KeyValuePair<string, WeakReference> vpReference = _internal.FirstOrDefault(wr => !wr.Value.IsAlive);
                string key = vpReference.Key;
                if (key != null)
                {
                    this._internal.Remove(key);
                    Console.WriteLine("WeakReference removed: " + key);
                    removeReferencesToBeDone = true;
                }
                else
                    removeReferencesToBeDone = false;
            }
            return true;
        }
    }
}
